package bancosys.tec.persist.spring.transaction;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import jmine.tec.utils.debug.impl.DebugRuntimeFactory;

import org.springframework.beans.factory.InitializingBean;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;

import bancosys.tec.persist.hibernate.PartitionSessionFactory;

/**
 * Decora um TransactionManager mudando de partição (quente/fria) conforme necessário quando a <code>TransactionDefinition</code> for uma
 * <code>PartitionTransactionDefinition</code>. A mudança de partição é executada no <code>getTransaction</code>.
 * 
 * @created 10/03/2008
 * @author Gustavo Almeida
 * @see PartitionTransactionDefinition
 */
public class PartitionTransactionManagerDecorator implements PlatformTransactionManager, InitializingBean {
    private PlatformTransactionManager transactionManager;

    private PartitionSessionFactory partitionSessionFactory;

    private static final Map<Object, String> OPEN_TRANSACTIONS = new ConcurrentHashMap<Object, String>();;

    /**
     * Define o transactionManager.
     * 
     * @param transactionManager O transactionManager a ser definido.
     */
    public void setTransactionManager(PlatformTransactionManager transactionManager) {
        this.transactionManager = transactionManager;
    }

    /**
     * Define o partitionSessionFactory.
     * 
     * @param partitionSessionFactory O partitionSessionFactory a ser definido.
     */
    public void setPartitionSessionFactory(PartitionSessionFactory partitionSessionFactory) {
        this.partitionSessionFactory = partitionSessionFactory;
    }

    /**
     * Delega o commit para o transaction manager que foi definido.
     * 
     * @param status transaction status
     */
    public void commit(TransactionStatus status) {
        this.transactionManager.commit(status);
        if (DebugRuntimeFactory.getInstance().active()) {
            OPEN_TRANSACTIONS.remove(status);
        }
    }

    /**
     * Define a partição atual se o definition for instancia de <code>PartitionTransactionDefinition</code> e delega o getTransaction() para
     * o transaction manager que foi definido.
     * 
     * @param definition transaction definition
     * @return transaction status devolvido pelo transaction manager definido.
     */
    public TransactionStatus getTransaction(TransactionDefinition definition) {
        if (definition instanceof PartitionTransactionDefinition) {
            PartitionTransactionDefinition partitionDefinition = (PartitionTransactionDefinition) definition;
            this.partitionSessionFactory.setThreadPartition(partitionDefinition.getPartition());
            this.partitionSessionFactory.setThreadPureSessionOnly(partitionDefinition.isPureSessionOnly());
        }
        TransactionStatus tx = this.transactionManager.getTransaction(definition);
        if (DebugRuntimeFactory.getInstance().active()) {
            OPEN_TRANSACTIONS.put(tx, DebugRuntimeFactory.getCurrentThreadStack());
        }
        return tx;
    }

    /**
     * Delega o rollback para o transaction manager que foi definido.
     * 
     * @param status transaction status
     */
    public void rollback(TransactionStatus status) {
        this.transactionManager.rollback(status);
        if (DebugRuntimeFactory.getInstance().active()) {
            OPEN_TRANSACTIONS.remove(status);
        }
    }

    /**
     * Devolve as informacoes sobre as transacoes abertas
     * 
     * @return array de String
     */
    public String[] getOpenTransactionsAsArray() {
        return OPEN_TRANSACTIONS.values().toArray(new String[OPEN_TRANSACTIONS.size()]);
    }

    /**
     * @return the open transactions
     */
    public static Map<Object, String> getOpenTransactions() {
        return OPEN_TRANSACTIONS;
    }

    /**
     * {@inheritDoc}
     */
    public void afterPropertiesSet() {
        DebugRuntimeFactory.getInstance().exposeAsJMX("partitionTransactionManager", this);
    }
}
